import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class SizedNotification extends Notification {
  SizedNotification(this.size);
  final Size size;
}

typedef SizedCallback = void Function(Size size);

class SizedNotifier extends SingleChildRenderObjectWidget {
  /// Creates a [SizeChangedLayoutNotifier] that dispatches layout changed
  /// notifications when [child] changes layout size.
  const SizedNotifier({
    Key? key,
    required Widget child,
  }) : super(key: key, child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _RenderSizedWithCallback(
      onSizedCallback: (Size? size) {
        if (size != null) {
          SizedNotification(size).dispatch(context);
        }
      },
      child: null,
    );
  }
}

class _RenderSizedWithCallback extends RenderProxyBox {
  _RenderSizedWithCallback({
    RenderBox? child,
    required this.onSizedCallback,
  }) : super(child);

  // There's a 1:1 relationship between the _RenderSizeChangedWithCallback and
  // the `context` that is captured by the closure created by createRenderObject
  // above to assign to onLayoutChangedCallback, and thus we know that the
  // onLayoutChangedCallback will never change nor need to change.

  final SizedCallback onSizedCallback;

  Size? _oldSize;

  @override
  void performLayout() {
    super.performLayout();
    // Don't send the initial notification, or this will be SizeObserver all
    // over again!
    if (_oldSize == null || size != _oldSize) {
      onSizedCallback(size);
    }
    _oldSize = size;
  }
}
